package cn.lg.soar.database.handler.type.sm4;

import cn.lg.soar.common.algorithm.SM4Utils;
import cn.lg.soar.common.util.data.DataUtil;
import cn.lg.soar.common.util.listener.SpringUtils;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.TypeHandler;

import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * 对称加密处理
 * @author luguoxiang
 * @date 2022-05-20
 * 开源项目：https://gitee.com/lgx1992/lg-soar 求star！请给我star！请帮我点个star！
 */
public abstract class AbstractSm4Handler<T> implements TypeHandler<T> {

    private static volatile SM4Utils utils;

    /**
     * 字节数组转目标类型
     * @param bytes
     * @return
     */
    abstract protected T ofBytes(byte[] bytes);

    /**
     * 目标类型转字节数组
     * @param value
     * @return
     */
    abstract protected byte[] toBytes(T value);

    /**
     * 加密
     * @param object
     * @return
     */
    protected String encode(T object) {
        if (object == null) {
            return null;
        }
        byte[] encode = getUtils().encrypt(toBytes(object));
        return DataUtil.byteToBase64(encode);
    }

    /**
     * 解密
     * @param base64
     * @return
     */
    protected T decode(String base64) {
        if (base64 == null) {
            return null;
        }
        byte[] bytes = DataUtil.base64ToByte(base64);
        byte[] decode = getUtils().decrypt(bytes);
        return ofBytes(decode);
    }

    /**
     * 获取密钥
     * @return
     */
    protected SM4Utils getUtils() {
        if (utils == null) {
            synchronized (AbstractSm4Handler.class) {
                if (utils == null) {
                    String s = SpringUtils.getProperty(
                            "lg.database.sm4-secret",
                            "ed1eda9e8bcd3fe0"
                    );
                    utils = SM4Utils.create(s);
                }
            }
        }
        return utils;
    }

    @Override
    public void setParameter(PreparedStatement preparedStatement, int i, T object, JdbcType jdbcType) throws SQLException {
        preparedStatement.setString(i, encode(object));
    }

    @Override
    public T getResult(ResultSet resultSet, String s) throws SQLException {
        return decode(resultSet.getString(s));
    }

    @Override
    public T getResult(ResultSet resultSet, int i) throws SQLException {
        return decode(resultSet.getString(i));
    }

    @Override
    public T getResult(CallableStatement callableStatement, int i) throws SQLException {
        return decode(callableStatement.getString(i));
    }

}
